/*******************************************************************************
 * Copyright (c) 2000, 2012 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package _org.eclipse.jdt.ui;

import melnorme.lang.ide.core.EclipseCore;
import melnorme.lang.ide.ui.LangImages;
import melnorme.lang.ide.ui.LangUIPlugin;
import melnorme.util.swt.jface.resources.ImageDescriptorRegistry;

import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceStatus;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.DecorationOverlayIcon;
import org.eclipse.jface.viewers.IBaseLabelProvider;
import org.eclipse.jface.viewers.IDecoration;
import org.eclipse.jface.viewers.ILabelDecorator;
import org.eclipse.jface.viewers.ILabelProviderListener;
import org.eclipse.jface.viewers.ILightweightLabelDecorator;
import org.eclipse.jface.viewers.LabelProviderChangedEvent;
import org.eclipse.swt.graphics.Image;

import _org.eclipse.jdt.internal.ui.viewsupport.IProblemChangedListener;


/**
 * LabelDecorator that decorates an element's image with error and warning overlays that
 * represent the severity of markers attached to the element's underlying resource. To see
 * a problem decoration for a marker, the marker needs to be a subtype of <code>IMarker.PROBLEM</code>.
 * <p>
 * <b>Important</b>: Although this decorator implements ILightweightLabelDecorator, do not contribute this
 * class as a decorator to the <code>org.eclipse.ui.decorators</code> extension. Only use this class in your
 * own views and label providers.
 *
 * @since 2.0
 */
public class ProblemsLabelDecorator implements ILabelDecorator, ILightweightLabelDecorator {

	/**
	 * This is a special <code>LabelProviderChangedEvent</code> carrying additional
	 * information whether the event origins from a maker change.
	 * <p>
	 * <code>ProblemsLabelChangedEvent</code>s are only generated by <code>
	 * ProblemsLabelDecorator</code>s.
	 * </p>
	 */
	public static class ProblemsLabelChangedEvent extends LabelProviderChangedEvent {

		private static final long serialVersionUID= 1L;

		private boolean fMarkerChange;

		/**
		 * @param eventSource the base label provider
		 * @param changedResource the changed resources
		 * @param isMarkerChange <code>true</code> if the change is a marker change; otherwise
		 *  <code>false</code>
		 */
		public ProblemsLabelChangedEvent(IBaseLabelProvider eventSource, IResource[] changedResource, boolean isMarkerChange) {
			super(eventSource, changedResource);
			fMarkerChange= isMarkerChange;
		}

		/**
		 * Returns whether this event origins from marker changes. If <code>false</code> an annotation
		 * model change is the origin. In this case viewers not displaying working copies can ignore these
		 * events.
		 *
		 * @return if this event origins from a marker change.
		 */
		public boolean isMarkerChange() {
			return fMarkerChange;
		}

	}

	private ImageDescriptorRegistry fRegistry;
	private boolean fUseNewRegistry= false;
	private IProblemChangedListener fProblemChangedListener;

	private ListenerList<ILabelProviderListener> fListeners;

	/**
	 * Creates a new <code>ProblemsLabelDecorator</code>.
	 */
	public ProblemsLabelDecorator() {
		this(null);
		fUseNewRegistry= true;
	}

	/**
	 * Note: This constructor is for internal use only. Clients should not call this constructor.
	 *
	 * @param registry The registry to use or <code>null</code> to use the Java plugin's image
	 *            registry
	 * @noreference This constructor is not intended to be referenced by clients.
	 */
	public ProblemsLabelDecorator(ImageDescriptorRegistry registry) {
		fRegistry= registry;
		fProblemChangedListener= null;
	}

	private ImageDescriptorRegistry getRegistry() {
		if (fRegistry == null) {
			fRegistry= fUseNewRegistry ? 
					new ImageDescriptorRegistry() : 
					LangUIPlugin.getDefault().getImageDescriptorRegistry();
		}
		return fRegistry;
	}


	@Override
	public String decorateText(String text, Object element) {
		return text;
	}

	@Override
	public Image decorateImage(Image image, Object obj) {
		if(image == null)
			return null;
		
		ImageDescriptor overlayImage = computeAdornmentFlags(obj);
		if(overlayImage != null) {
//			ImageDescriptor baseImage = new ImageImageDescriptor(image);
			return getRegistry().get(new DecorationOverlayIcon(image, overlayImage, IDecoration.BOTTOM_LEFT));
		}
		return image;
	}

	/**
	 * Computes the adornment flags for the given element.
	 *
	 * @param obj the element to compute the flags for
	 *
	 * @return the adornment flags
	 */
	protected ImageDescriptor computeAdornmentFlags(Object obj) {
		try {
			if (obj instanceof IResource) {
				return getErrorTicksFromMarkers((IResource) obj, IResource.DEPTH_INFINITE);
			}
		} catch (CoreException e) {
			if (e.getStatus().getCode() == IResourceStatus.MARKER_NOT_FOUND) {
				return null;
			}

			EclipseCore.logStatus(e);
		}
		return null;
	}
	
	protected ImageDescriptor getErrorTicksFromMarkers(IResource res, int depth) throws CoreException {
		if (res == null || !res.isAccessible()) {
			return null;
		}
		
		int severity= 0;
//			if (res instanceof IProject) {
//				severity= res.findMaxProblemSeverity(IJavaModelMarker.BUILDPATH_PROBLEM_MARKER, true, IResource.DEPTH_ZERO);
//				if (severity == IMarker.SEVERITY_ERROR) {
//					return ERRORTICK_BUILDPATH_ERROR;
//				}
//				severity= res.findMaxProblemSeverity(JavaRuntime.JRE_CONTAINER_MARKER, true, IResource.DEPTH_ZERO);
//				if (severity == IMarker.SEVERITY_ERROR) {
//					return ERRORTICK_BUILDPATH_ERROR;
//				}
//			}
			severity= res.findMaxProblemSeverity(IMarker.PROBLEM, true, depth);
		if (severity == IMarker.SEVERITY_ERROR) {
			return LangImages.DESC_OVR_ERROR;
		} else if (severity == IMarker.SEVERITY_WARNING) {
			return LangImages.DESC_OVR_WARNING;
		}
		return null;
	}

//	private int getPackageErrorTicksFromMarkers(IPackageFragment pack) throws CoreException {
//		// Packages are special: They must not consider markers on subpackages.
//		
//		IResource res= pack.getResource();
//		if (res == null || !res.isAccessible()) {
//			return 0;
//		}
//		
//		// markers on package itself (e.g. missing @NonNullByDefault)
//		int severity= res.findMaxProblemSeverity(IMarker.PROBLEM, true, IResource.DEPTH_ZERO);
//		if (severity == IMarker.SEVERITY_ERROR)
//			return ERRORTICK_ERROR;
//		
//		// markers on CUs
//		for (ICompilationUnit cu : pack.getCompilationUnits()) {
//			severity= Math.max(severity, cu.getResource().findMaxProblemSeverity(IMarker.PROBLEM, true, IResource.DEPTH_ZERO));
//			if (severity == IMarker.SEVERITY_ERROR)
//				return ERRORTICK_ERROR;
//		}
//		
//		// markers on files and folders
//		for (Object object : pack.getNonJavaResources()) {
//			if (object instanceof IResource) {
//				IResource resource= (IResource) object;
//				severity= Math.max(severity, resource.findMaxProblemSeverity(IMarker.PROBLEM, true, IResource.DEPTH_INFINITE));
//				if (severity == IMarker.SEVERITY_ERROR)
//					return ERRORTICK_ERROR;
//			}
//		}
//		
//		// SEVERITY_ERROR already handled above
//		if (severity == IMarker.SEVERITY_WARNING) {
//			return ERRORTICK_WARNING;
//		}
//		return 0;
//	}

	@Override
	public void dispose() {
		if (fProblemChangedListener != null) {
			LangUIPlugin.getDefault().getProblemMarkerManager().removeListener(fProblemChangedListener);
			fProblemChangedListener= null;
		}
		if (fRegistry != null && fUseNewRegistry) {
			fRegistry.dispose();
		}
	}

	@Override
	public boolean isLabelProperty(Object element, String property) {
		return true;
	}

	@Override
	public void addListener(ILabelProviderListener listener) {
		if (fListeners == null) {
			fListeners= new ListenerList<>();
		}
		fListeners.add(listener);
		if (fProblemChangedListener == null) {
			fProblemChangedListener= new IProblemChangedListener() {
				@Override
				public void problemsChanged(IResource[] changedResources, boolean isMarkerChange,
						boolean calledFromDisplayThread) {
					fireProblemsChanged(changedResources, isMarkerChange);
				}
			};
			LangUIPlugin.getDefault().getProblemMarkerManager().addListener(fProblemChangedListener);
		}
	}

	@Override
	public void removeListener(ILabelProviderListener listener) {
		if (fListeners != null) {
			fListeners.remove(listener);
			if (fListeners.isEmpty() && fProblemChangedListener != null) {
				LangUIPlugin.getDefault().getProblemMarkerManager().removeListener(fProblemChangedListener);
				fProblemChangedListener= null;
			}
		}
	}

	private void fireProblemsChanged(IResource[] changedResources, boolean isMarkerChange) {
		if (fListeners != null && !fListeners.isEmpty()) {
			LabelProviderChangedEvent event= new ProblemsLabelChangedEvent(this, changedResources, isMarkerChange);
			Object[] listeners= fListeners.getListeners();
			for (int i= 0; i < listeners.length; i++) {
				((ILabelProviderListener) listeners[i]).labelProviderChanged(event);
			}
		}
	}

	@Override
	public void decorate(Object element, IDecoration decoration) {
		ImageDescriptor imageDescriptor= computeAdornmentFlags(element);
		if(imageDescriptor != null) {
			decoration.addOverlay(imageDescriptor);
		}
	}

}